
#pragma once

template <typename T>
class DynArray
{
private:
	unsigned int num;
	unsigned int cap;
	T *data;

public:
	///////////////////////////////////////////////////////////////////////////////
	// Function :	Constructor
	// Notes : Constructs an empty array (Size 0 Capacity 0)
	///////////////////////////////////////////////////////////////////////////////
	DynArray() : num(0), cap(0), data(0) {}

	///////////////////////////////////////////////////////////////////////////////
	// Function :	Destructor
	// Notes : cleans up any dynamic memory
	///////////////////////////////////////////////////////////////////////////////
	~DynArray();

	///////////////////////////////////////////////////////////////////////////////
	// Function :	Copy Constructor
	///////////////////////////////////////////////////////////////////////////////
	DynArray(const DynArray<T>& that);

	///////////////////////////////////////////////////////////////////////////////
	// Function :	Assignment Operator
	///////////////////////////////////////////////////////////////////////////////
	DynArray<T>& operator=(const DynArray<T>& that);

	///////////////////////////////////////////////////////////////////////////////
	// Function : operator[]
	// Parameters : index - the index to access
	// Return : Type & - returns the array element at [index]
	// Notes : performs no error checking. user should ensure index is 
	//		valid with the size() method
	///////////////////////////////////////////////////////////////////////////////
	T& operator[](const unsigned int& index) { return data[index];}

	///////////////////////////////////////////////////////////////////////////////
	// Function :	size
	// Returns : int - returns the number of items being stored
	// Notes : this function returns the number of items being stored, 
	///////////////////////////////////////////////////////////////////////////////
	unsigned int size() const { return num; }

	///////////////////////////////////////////////////////////////////////////////
	// Function : capacity
	// Returns : int - returns the number of items the array can store before 
	//		the next resize
	// Notes : this function returns the number of items the array can store, 
	//		not the number of bytes
	///////////////////////////////////////////////////////////////////////////////
	unsigned int capacity() const { return cap; }

	///////////////////////////////////////////////////////////////////////////////
	// Function :	clear
	// Notes : cleans up any dynamic memory and resets size and capacity to 0
	///////////////////////////////////////////////////////////////////////////////
	void clear();

	///////////////////////////////////////////////////////////////////////////////
	// Function : append
	// Parameters : item - the item to be appended to the next open spot
	// Notes : this function will append the item to the next open spot. if 
	//		no room exists, the array's capacity will be doubled and then 
	//		the item will be added
	///////////////////////////////////////////////////////////////////////////////
	void append(const T& item);

	///////////////////////////////////////////////////////////////////////////////
	// Function : reserve
	// Parameters : newCap - the new capacity
	// Notes : 	- default parameter - reserve more space in the array, based on the 
	//		expansion rate (100%, 1 minimum).
	//		- non-default parameter, expand to the specified capacity
	//		- if newCap is LESS than the current capacity, do nothing. 
	//		This function should NOT make the array smaller.
	///////////////////////////////////////////////////////////////////////////////
	void reserve(const unsigned int & newCap = 0);
};



///////////////////////////////////////////////////////////////////////////////
// Function :	Destructor
// Notes : cleans up any dynamic memory
///////////////////////////////////////////////////////////////////////////////
template <typename T>
DynArray<T>::~DynArray()
{
	delete[] data;
	data = 0;
}

///////////////////////////////////////////////////////////////////////////////
// Function :	Copy Constructor
///////////////////////////////////////////////////////////////////////////////
template <typename T>
DynArray<T>::DynArray(const DynArray<T>& that) : data(0), num(0), cap(0)
{
	if(that.cap > 0)
	{
		data = new T[that.cap];
		cap = that.cap;
		num = that.num;
		for(unsigned int i = 0; i < cap; i++)
		{
			data[i] = that.data[i];
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function :	Assignment Operator
///////////////////////////////////////////////////////////////////////////////
template <typename T>
DynArray<T>& DynArray<T>::operator=(const DynArray<T>& that)
{
	if(this != &that)
	{	
		
		data = new T[that.cap];
		cap = that.cap;
		for(unsigned int i = 0; i < that.cap; i++)
		{
			data[i] = that.data[i];
		}
		num = that.num;

	}

	return *this;
}


///////////////////////////////////////////////////////////////////////////////
// Function :	clear
// Notes : cleans up any dynamic memory and resets size and capacity to 0
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void DynArray<typename T>::clear()
{
	delete[] data;
	data = 0;
	cap = 0;
	num = 0;
}

///////////////////////////////////////////////////////////////////////////////
// Function : append
// Parameters : item - the item to be appended to the next open spot
// Notes : this function will append the item to the next open spot. if 
//		no room exists, the array's capacity will be doubled and then 
//		the item will be added
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void DynArray<typename T>::append(const T& item)
{
	if(num >= cap)
	{
		reserve();
	}
	data[num++] = item;
}

///////////////////////////////////////////////////////////////////////////////
// Function : reserve
// Parameters : newCap - the new capacity
// Notes : 	- default parameter - reserve more space in the array, based on the 
//		expansion rate (100%, 1 minimum).
//		- non-default parameter, expand to the specified capacity
//		- if newCap is LESS than the current capacity, do nothing. 
//		This function should NOT make the array smaller.
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void DynArray<typename T>::reserve(const unsigned int & newCap = 0)
{
	if(newCap > cap || newCap == 0)
	{
		unsigned int oldCap = cap;
		if(newCap == 0 && cap != 0)
			cap <<= 1;
		else if(newCap == 0)
			cap = 1;
		else
			cap = newCap;

		T *temp = new T[cap];
		for(unsigned int i = 0; i < oldCap; i++)
		{
			temp[i] = data[i];
		}
		
		delete[] data;
		data = temp;
	}
}
